package robotCross;

import abbot.Log;
import abbot.i18n.Strings;
import abbot.tester.*;
import abbot.util.Condition;
import abbot.util.ExtendedComparator;
import com.supermap.desktop.controls.ui.controls.TableRowCellEditor;
import operation.Operation;

import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.util.Hashtable;
import java.util.concurrent.Callable;

/**
 * JComboBoxTester方法不能满足JTbale中表格的点击，主要是makePopupVisible抛异常。combobox其实并没有显示出来，无法获取位置。
 * 重新添加makePopupVisible方法
 * Created by hanyz on 2016/12/7.
 */
public class CrossJTableComboBoxTester extends JComboBoxTester {
    public JTable jTable;
    public int row = 0;
    public int col = 0;

    private class PopupNotFoundException extends ActionFailedException {
        public PopupNotFoundException(String m) {
            super(m);
        }
    }

    /**
     * 双击单元格右侧，弹出combobox
     */
    private Component makePopupVisible(JComboBox cb) {
        Rectangle bounds = new JTableLocation(row, col).getBounds(jTable);
        //模拟双击，显示combobox下拉菜单
        Operation.robot.click(jTable, bounds.x + bounds.width - 5, bounds.y + bounds.height / 2);
        Operation.robot.delay(100);
        Operation.robot.mousePress(InputEvent.BUTTON1_MASK);//按下左键
        Operation.robot.mouseRelease(InputEvent.BUTTON1_MASK);//释放左键

        final Component popup = findPopupComponent(cb);

        wait(new ConditionEDTDecorator(cb, new Condition() {
            public boolean test() {
                return isReadyForInput(popup);
            }

            public String toString() {
                return Strings.get("tester.JComboBox.popup_not_ready");
            }
        }), defaultDelay / 4); // Ramp up delay as still getting
        return popup;
    }

    public void actionSelectIndex(JTable table, int row, int col, int index) {
        this.jTable = table;
        this.row = row;
        this.col = col;
        TableRowCellEditor cellEditor = (TableRowCellEditor) table.getColumn(table.getColumnName(col)).getCellEditor();
        Hashtable editors = cellEditor.getEditors();
        JComboBox comboBox = (JComboBox) ((DefaultCellEditor) editors.get(row)).getComponent();
        //选择下拉项
        actionSelectIndex(comboBox, index);
    }

    public void actionSelectItem(JTable table, int row, int col, String item) {
        this.jTable = table;
        this.row = row;
        this.col = col;
        TableRowCellEditor cellEditor = (TableRowCellEditor) table.getColumn(table.getColumnName(1)).getCellEditor();
        Hashtable editors = cellEditor.getEditors();
        JComboBox comboBox = (JComboBox) ((DefaultCellEditor) editors.get(row)).getComponent();
        //选择下拉项
        actionSelectItem(comboBox, item);
    }

    public void actionSelectIndex(Component comp, final int index) {
        final JComboBox cb = (JComboBox) comp;

        Component popup = makePopupVisible(cb);

        //

        try {
            // Not all LAFs use a JList for the popup
            try {
                selectIndexInPopup(popup, index);
            } catch (ComponentNotShowingException cnse) {

                //                Log.warn("Problem selecting item in a combobox, going to retry then throw original exception");
                //
                //                popup = makePopupVisible(cb);
                //                try {
                //                    selectIndexInPopup(popup, index);
                //                }
                //                catch (ComponentNotShowingException cnse2) {
                throw cnse;
                //                }
            }
        } catch (PopupNotFoundException e) {
            invokeAndWait(new Runnable() {
                public void run() {
                    cb.setSelectedIndex(index);
                    if (cb.getUI().isPopupVisible(cb))
                        cb.getUI().setPopupVisible(cb, false);
                }
            });
        }

        // Wait for all the events to processed

        waitForIdle();
    }

    public void actionSelectItem(Component comp, final String item) {

        final JComboBox cb = (JComboBox) comp;
        final JComponent popup = findPopupFromUI(cb);

        // Get rid of any previous popup
        invokeAndWait(comp, new Runnable() {
            @Override
            public void run() {
                if (cb.isPopupVisible()) {
                    cb.hidePopup();
                }
            }
        });

        // Need to be done in one block otherwise the selection might changed
        boolean alreadySelected = abbot.tester.Robot.callAndWait(cb, new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                final Object obj = cb.getSelectedItem();
                if ((obj == null && item == null) ||
                        (obj != null &&
                                ExtendedComparator.stringsMatch(item, getValueAsString(cb, popup, obj, cb.getSelectedIndex()))))
                    return true;

                return false;
            }
        });
        if (alreadySelected) {
            return;
        }


        final CrossJTableComboBoxTester.GetIndexForItem mo = new CrossJTableComboBoxTester.GetIndexForItem(cb, popup, item);

        // Wait for make sure that the combo is populated
        wait(new ConditionEDTDecorator(cb, new Condition() {
            public boolean test() {
                return mo.call() > -1;
            }

            public String toString() {
                return Strings.get("tester.JComboBox.item_not_found", new Object[]{item, dumpList(cb)});
            }
        }), defaultDelay / 4); // Ramp up delay as still getting


        int matchedIndex = abbot.tester.Robot.callAndWait(cb, mo);

        if (matchedIndex != -1) {
            actionSelectIndex(comp, matchedIndex);
            return;
        }

        // While actions are supposed to represent real user actions, it's
        // possible that the current environment does not match sufficiently,
        // so we need to throw an appropriate exception that can be used to
        // diagnose the problem.
        String contents = dumpList(cb);
        throw new ActionFailedException(Strings.get("tester.JComboBox.item_not_found", new Object[]{
                item, contents}));
    }


    private class GetIndexForItem implements Callable<Integer> {
        private JComboBox _cb;
        private JComponent _list;
        private String _item;

        GetIndexForItem(JComboBox cb, JComponent list, String item) {
            this._cb = cb;
            this._list = list;
            this._item = item;
        }

        @Override
        public Integer call() {

            RenderToString correctRender = new RenderToString() {
                @Override
                public String render(Object obj, int index) {
                    return getValueAsString(_cb, _list, obj, index);
                }
            };
            int found = find(correctRender);

            // Previously JComboBoxTester use to default to using toString
            // so we are going to fall back where to using this so that
            // the test is some-what backwards'ly compatible
            if (found == -1) {
                found = find(new RenderToString() {

                    @Override
                    public String render(Object item, int index) {
                        String value = item.toString();
                        return value;
                    }
                });

                if (found != -1) {
                    Log.warn("JComboBoxTester falling back to legacy toString code for item matching " +
                            _item.toString() + " but should be " + correctRender.render(_item, found));
                }
            }

            return found;
        }


        private int find(RenderToString render) {

            int found = -1;

            for (int i = 0; i < this._cb.getItemCount(); i++) {
                Object obj = this._cb.getItemAt(i);
                String objString = render.render(obj, i);

                Log.debug("Comparing against '" + objString + "'");
                if ((obj == null && this._item == null) ||
                        (obj != null && ExtendedComparator.stringsMatch(this._item, objString))) {
                    found = i;
                    break;
                }
            }
            return found;
        }
    }

    private interface RenderToString {
        public String render(Object obj, int index);
    }

}

